home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / System 7.0 Samples / Kibitz⁄THINK C / Chess.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-21  |  45.4 KB  |  1,647 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:        Kibitz
  5. ** File:        chess.c
  6. ** Written by:  Eric Soldan
  7. **
  8. ** Copyright © 1990-1991 Apple Computer, Inc.
  9. ** All rights reserved.
  10. */
  11.  
  12.  
  13.  
  14. /*****************************************************************************/
  15.  
  16.  
  17.  
  18. #include "Kibitz.h"                /* Get the Kibitz includes/typedefs, etc.    */
  19. #include "KibitzCommon.h"        /* Get the stuff in common with rez.        */
  20. #include "Kibitz.protos"        /* Get the prototypes for Kibitz.            */
  21.  
  22. #ifndef __ERRORS__
  23. #include <Errors.h>
  24. #endif
  25.  
  26. #ifndef __FONTS__
  27. #include <Fonts.h>
  28. #endif
  29.  
  30. #ifndef __TEXTEDITCONTROL__
  31. #include "TextEditControl.h"
  32. #endif
  33.  
  34. #ifndef __TOOLUTILS__
  35. #include <ToolUtils.h>
  36. #endif
  37.  
  38.  
  39.  
  40. /*****************************************************************************/
  41.  
  42.  
  43.  
  44. static unsigned long    idleTick;
  45.  
  46.  
  47.  
  48. /*****************************************************************************/
  49.  
  50.  
  51.  
  52. TheDoc    newDocData = {
  53.     kVersion,        /* File format version.                             */
  54.  
  55.     false,            /* Flag indicating print record is current.         */
  56.     {                /* Space for print record.                         */
  57.         0,
  58.         {0, 0, 0,{0, 0, 0, 0},},
  59.         {0, 0, 0, 0},
  60.         {0, 0, 0, 0, 0},
  61.         {0, 0, 0,{0, 0, 0, 0},},
  62.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  63.         {0, 0, 0, 0, 0, nil, nil, 0, 0, 0},
  64.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  65.     },
  66.  
  67.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  68.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  69.     OBNDS,  BR,    BN,    BB,    BQ,    BK,    BB,    BN,    BR,   OBNDS,
  70.     OBNDS,  BP,    BP,    BP,    BP,    BP,    BP,    BP,    BP,   OBNDS,
  71.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  72.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  73.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  74.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  75.     OBNDS,  WP,    WP,    WP,    WP,    WP,    WP,    WP,    WP,   OBNDS,
  76.     OBNDS,  WR,    WN,    WB,    WQ,    WK,    WB,    WN,    WR,   OBNDS,
  77.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  78.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  79.     {
  80.         WKPOS, 0,    /* White king position, king-moved count.         */
  81.         0,            /* White queen-rook-moved count.                 */
  82.         0,            /* White king-rook-moved count.                     */
  83.         BKPOS, 0,    /* Black king position, king-moved count.         */
  84.         0,            /* Black queen-rook-moved count.                 */
  85.         0            /* Black king-rook-moved count.                     */
  86.     },
  87.     0,                /* En-passant opportunity location.                 */
  88.     0,                /* En-passant opportunity pawn-to-take location. */
  89.     0,                /* Arranged en-passant opportunity loc.             */
  90.     0,                /* Arranged en-passant opp. pawn-to-take loc.     */
  91.     0,                /* Number of legal moves in move list.             */
  92.     0,                /* Index into record of game.                     */
  93.     0,                /* Number of moves in game.                         */
  94.     0,                /* My color (0 = white).                         */
  95.     0,                /* True if black started game.                     */
  96.     0,                /* True if in arrange-board mode.                 */
  97.     WP,                /* Piece hilited in arrange-board palette.         */
  98.     18000L,            /* 5 minute default time for white.                 */
  99.     18000L,            /* 5 minute default time for black.                 */
  100.     -1L,            /* Ticks remaining for white. (-1, no clock)     */
  101.     -1L,            /* Ticks remaining for black. (-1, no clock)     */
  102.     false,            /* Display board normal (not inverted.)             */
  103.     0,                /* Above info is saved to disk.                     */
  104.  
  105.     false,            /* Flag indicating existence of opponent.         */
  106.     0L,                /* ID assigned by me for this game.                 */
  107.     0L,                /* ID assigned by opponent for this game.         */
  108.     0,                /* Reason for sending the game.                     */
  109.     0,                /* State of the draw button.                      */
  110.     {
  111.         0L,            /* AEAddressDesc of opponent.                     */
  112.         nil
  113.     },
  114.     0,                /* Above is send game info.                         */
  115.  
  116.     0,                /* State for receiving AppleEvents.                 */
  117.     false,            /* Flag indicating if we originated game.         */
  118.     -1L,            /* Ticks remaining displayed for white.             */
  119.     -1L,            /* Ticks remaining displayed for black.             */
  120.     0L,                /* Reference tick for timer.                     */
  121.     0L,                /* Tick when computer moved last.                 */
  122.     0L,                /* Tick when received last info from opponent.     */
  123.     false,            /* Flag indicating computer moves white pieces.     */
  124.     false,            /* Flag indicating computer moves black pieces.     */
  125.     "012345678901234567890123456789012",    /* Space for user name.     */
  126.     0,                /* Above info is for one machine only.             */
  127.  
  128.     false,            /* Flag indicating color change has been posted. */
  129.     0,                /* New color from config.                         */
  130.     false,            /* Flag indicating time change has been posted.     */
  131.     -1L,            /* New white time from config.                     */
  132.     -1L,            /* New white time from config.                     */
  133.     0,                /* Above info is config setting which will         */
  134.                     /* not be applied until a NULL event.             */
  135.  
  136.     nil,            /* Handle to legal move list.                     */
  137.     nil,            /* Handle to game record.                         */
  138.     nil,            /* Handle to incoming message.                     */
  139.     nil,            /* Handle to outgoing message.                     */
  140.     nil,            /* Handle to recorded sound, if any.             */
  141.     nil,            /* Handle to send button control.                 */
  142.     nil,            /* Handle to move notify control.                 */
  143.     nil,            /* Handle to message notify control.             */
  144.     nil,            /* Handle to game-slider control.                 */
  145.     nil,            /* Handle to white-starts radio button.             */
  146.     nil,            /* Handle to black-starts radio button.             */
  147.     nil,            /* Handle to resign button.                         */
  148.     nil,            /* Handle to draw button.                         */
  149.     nil,            /* Handle to record sound button.                 */
  150.     nil,            /* Handle to send sound button.                     */
  151.     0,                /* Above info is reference to controls.             */
  152. };
  153.  
  154. short    direction[10][9] = {
  155.       0,   0,   0,   0,   0,   0,   0,   0,   0,
  156.      10,   9,  11,   0,   0,   0,   0,   0,   0,    /* Pawn moves.     */
  157.     -21, -19, -12,  -8,   8,  12,  19,  21,   0,    /* Knight moves. */
  158.     -11,  -9,   9,  11,   0,   0,   0,   0,   0,    /* Bishop moves. */
  159.     -10,  -1,   1,  10,   0,   0,   0,   0,   0,    /* Rook moves.     */
  160.     -11,  -9,   9,  11, -10,  -1,   1,  10,   0,    /* Queen moves.     */
  161.     -11,  -9,   9,  11, -10,  -1,   1,  10,   0,    /* King moves.     */
  162. };
  163.  
  164. short    distance[10] = {0, 1, 1, 7, 7, 7, 1};
  165.     /* How far a piece can move.                        */
  166.     /* The double-pawn-push is handled as an exception. */
  167.     /* Castling is handled as an exception.                */
  168.  
  169.  
  170.  
  171. /*****************************************************************************/
  172.  
  173.  
  174.  
  175. #pragma segment Chess
  176. void    AddLegalMove(FileRecHndl game, short from, short to)
  177. {
  178.     MoveListHndl    lglMoves;
  179.     short            numLglMoves;
  180.     long            newHndlSize, oldHndlSize;
  181.  
  182.     numLglMoves = (*game)->doc.numLegalMoves;
  183.     lglMoves    = (*game)->doc.legalMoves;
  184.  
  185.     oldHndlSize = GetHandleSize((Handle)lglMoves);
  186.     newHndlSize = ((numLglMoves | 0x3F) + 1) * sizeof(MoveElement);
  187.     if (newHndlSize != oldHndlSize)
  188.         SetHandleSize((Handle)lglMoves, newHndlSize);
  189.  
  190.     (**lglMoves)[numLglMoves].moveFrom = from;
  191.     (**lglMoves)[numLglMoves].moveTo   = to;
  192.     (**lglMoves)[numLglMoves].value    = 0;
  193.  
  194.     ++(*game)->doc.numLegalMoves;
  195. }
  196.  
  197.  
  198.  
  199. /*****************************************************************************/
  200.  
  201.  
  202.  
  203. #pragma segment Chess
  204. Boolean    CastleOkay(FileRecHndl game, short castleSide)
  205. {
  206.     short    color;
  207.     short    castleDir, kingLoc, rookLoc;
  208.     short    i, j, piece, pieceColor;
  209.  
  210.     color = WhosMove(game);            /* Who's move it is. */
  211.  
  212.     if ((*game)->doc.king[color].kingMoves) return(false);
  213.         /* Can't castle.  King has already moved. */
  214.  
  215.     rookLoc = (kingLoc = (*game)->doc.king[color].kingLoc) + 3;
  216.     if ((castleDir = castleSide) == QSIDE) {
  217.         rookLoc -= 7;
  218.         --castleDir;
  219.     }
  220.  
  221.     for (i = kingLoc, j = 3; j; i += castleDir, --j)
  222.         if (SquareAttacked(game, i, color)) return(false);
  223.             /* Can't castle out of, through, or into check. */
  224.  
  225.     if ((*game)->doc.king[color].rookMoves[castleSide]) return(false);
  226.         /* Rook trying to castle with has already moved,
  227.         ** or has been taken.
  228.         */
  229.  
  230.     piece = (*game)->doc.theBoard[rookLoc];
  231.     pieceColor = BLACK;
  232.     if (piece < 0) {
  233.         pieceColor = WHITE;
  234.         piece = -piece;
  235.     }
  236.     if (color != pieceColor) return(false);
  237.     if (piece != ROOK)         return(false);
  238.         /* These deviant conditions can occur if user arranged the board. */
  239.  
  240.     /* So far, everything is cool.  The only remaining possible problem is
  241.     ** that there is a piece (or more) between the king and the rook.
  242.     */
  243.  
  244.     while (kingLoc += castleDir, kingLoc != rookLoc)
  245.         if ((*game)->doc.theBoard[kingLoc]) return(false);
  246.             /* There is a piece in the way, so we can't castle. */
  247.  
  248.     return(true);        /* The castling move is okay. */
  249. }
  250.  
  251.  
  252.  
  253. /*****************************************************************************/
  254.  
  255.  
  256.  
  257. #pragma segment Chess
  258. short    GameStatus(FileRecHndl game)
  259. {
  260.     short            i, color, kingLoc;
  261.     short            board[120], *boardPtr;
  262.     short            origGameIndex, gameIndex;
  263.     short            rep, back, pieceMoved, gameStat;
  264.     GameListHndl    gameMoves;
  265.  
  266.     if ((*game)->doc.arrangeBoard)
  267.         return(kGameContinues);
  268.  
  269.     for (i = 0; i < 2; ++i)
  270.         if (!(*game)->doc.timeLeft[i])
  271.             return(kYouLoseOnTime - (i ^ (*game)->doc.myColor));
  272.  
  273.     GenerateLegalMoves(game);
  274.  
  275.     gameMoves     = (*game)->doc.gameMoves;
  276.     origGameIndex = (*game)->doc.gameIndex;
  277.  
  278.     if (!(*game)->doc.numLegalMoves) {
  279.  
  280.         color = WhosMove(game);            /* Who's move it is. */
  281.  
  282.         if ((origGameIndex) && (origGameIndex == (*game)->doc.numGameMoves))
  283.             if (!(**gameMoves)[origGameIndex - 1].moveFrom)
  284.                 return((**gameMoves)[origGameIndex - 1].moveTo);
  285.  
  286.         kingLoc = (*game)->doc.king[color].kingLoc;
  287.  
  288.         if (SquareAttacked(game, kingLoc, color)) {        /* Checkmated. */
  289.             if (color == (*game)->doc.myColor) return(kYouLose);
  290.             else return(kYouWin);
  291.         }
  292.  
  293.         return(kStalemate);
  294.     }
  295.  
  296.     BlockMove((Ptr)&(*game)->doc.theBoard[0], (Ptr)&board[0], 120 * sizeof(short));
  297.  
  298.     rep = 2;    /* 2 matching current position makes 3 that match. */
  299.     back = 100;
  300.  
  301.     while ((rep) && (back) && (gameIndex = (*game)->doc.gameIndex)) {
  302.  
  303.         if ((**gameMoves)[--gameIndex].pieceCaptured) break;
  304.  
  305.         pieceMoved = (*game)->doc.theBoard[(**gameMoves)[gameIndex].moveTo];
  306.         if (pieceMoved < 0) pieceMoved = -pieceMoved;
  307.         if (pieceMoved == PAWN) break;
  308.  
  309.         UnmakeMove(game);
  310.         --back;
  311.  
  312.         boardPtr = &(*game)->doc.theBoard[0];
  313.         for (i = START_IBNDS; i < END_IBNDS; ++i)
  314.             if (boardPtr[i] != board[i]) break;
  315.  
  316.         if (i == END_IBNDS)
  317.             if (!(--rep)) break;
  318.     }
  319.  
  320.     while ((*game)->doc.gameIndex != origGameIndex)
  321.         MakeMove(game, 1, 0, 0, false);
  322.  
  323.     gameStat = kGameContinues;
  324.     if (!rep)  gameStat = kDrawByRep;
  325.     if (!back) gameStat = kDrawBy50;
  326.  
  327.     if (gameStat) (*game)->doc.numLegalMoves = 0;
  328.  
  329.     return(gameStat);
  330. }
  331.  
  332.  
  333.  
  334. /*****************************************************************************/
  335.  
  336.  
  337.  
  338. #pragma segment Chess
  339. void    GenerateLegalMoves(FileRecHndl game)
  340. {
  341.     short            gameIndex, square, piece;
  342.     short            color, pieceColor;
  343.     short            row, dirNum, dir, dist;
  344.     short            s, d, dest, destColor, epLoc;
  345.     Boolean            check;
  346.     GameListHndl    gameMoves;
  347.     GameElement        cacheMove;
  348.  
  349.     gameMoves = (*game)->doc.gameMoves;
  350.     gameIndex = (*game)->doc.gameIndex;
  351.     cacheMove = (**gameMoves)[gameIndex];
  352.  
  353.     (*game)->doc.numLegalMoves = 0;            /* Start the list over. */
  354.  
  355.     if ((gameIndex) && (gameIndex == (*game)->doc.numGameMoves))
  356.         if (!(**gameMoves)[gameIndex - 1].moveFrom) return;
  357.             /* Resignation or agreed-upon draw recorded.
  358.             ** Game over, so no legal moves. */
  359.  
  360.     color = WhosMove(game);                        /* Who's move it is. */
  361.  
  362.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  363.         /* Scan for pieces of correct color. */
  364.  
  365.         if ((piece = (*game)->doc.theBoard[square]) == EMPTY) continue;
  366.             /* Empty square, so next square, please. */
  367.  
  368.         if (piece == OBNDS) continue;
  369.             /* Out of bounds. */
  370.  
  371.         pieceColor = BLACK;
  372.         if (piece < 0) {
  373.             pieceColor = WHITE;
  374.             piece = -piece;
  375.         }
  376.  
  377.         if (pieceColor != color) continue;
  378.             /* Not our piece. */
  379.  
  380.         row = square / 10;
  381.  
  382.         for (dirNum = 0; dir = direction[piece][dirNum]; ++dirNum) {
  383.             /* The direction we will move a piece.  This is correct in all
  384.             ** cases except for white pawns.  Without an adjustment, they
  385.             ** would move in the direction of black pawns, i.e., backwards.
  386.             */
  387.  
  388.             dist = distance[piece];
  389.                 /* The distance a piece can move, in all cases except a
  390.                 ** double-pawn-push and castling.
  391.                 */
  392.  
  393.             if (piece == PAWN) {
  394.                 if (color == WHITE) dir = -dir;
  395.                     /* White pawns will now move forwards. */
  396.  
  397.                 if ((!dirNum) && ((row == 3) || (row == 8))) dist = 2;
  398.                     /* In the case of a pawn, the first direction we check
  399.                     ** is forwards, so if dirNum is 0, we are pushing pawns.
  400.                     **
  401.                     ** Allow double-pawn-push if pawn is on correct row.  We don't
  402.                     ** have to worry about which color the pawn is if the pawn
  403.                     ** has advanced to the other double-push row.  It will
  404.                     ** double-push itself out of bounds.
  405.                     */
  406.             }
  407.  
  408.             for (s = square, d = 1; d <= dist; ++d) {
  409.  
  410.                 s += dir;
  411.                 dest = (*game)->doc.theBoard[s];
  412.                 if (dest == OBNDS) break;    /* Can't go this direction anymore. */
  413.  
  414.                 destColor = BLACK;
  415.                 if (dest < 0) {
  416.                     destColor = WHITE;
  417.                     dest = -dest;
  418.                 }
  419.  
  420.                 if ((dest) && (destColor == color)) break;
  421.                     /* Ran into our own piece, so can't go this direction anymore. */
  422.  
  423.                 if (dest == KING) break;
  424.                     /* Never allow the king to be taken. */
  425.  
  426.                 if (piece == PAWN) {
  427.                     if (!dirNum) {                /* If pawn push... */
  428.                         if (dest) break;        /* Can't take on a pawn-push. */
  429.                     }
  430.                     else {
  431.                         if (dest == EMPTY) {    /* If possible en-passant... */
  432.                             if ((*game)->doc.enPasMove != s) break;
  433.                                 /* Not en-passant pawn capture. */
  434.                             epLoc = (*game)->doc.enPasPawnLoc;
  435.                             dest  = (*game)->doc.theBoard[epLoc];
  436.                             destColor = BLACK;
  437.                             if (dest < 0) {
  438.                                 destColor = WHITE;
  439.                                 dest = -dest;
  440.                             }
  441.                             if (destColor == color) break;
  442.                                 /* We can't en-passant our own piece. */
  443.                             if (dest != PAWN) break;
  444.                                 /* We can only en-passant pawns. */
  445.                         }
  446.                     }
  447.                 }
  448.  
  449.                 MakeMove(game, square, s, QUEEN, false);
  450.                 check = SquareAttacked(game,
  451.                     (*game)->doc.king[color].kingLoc, color);
  452.                 UnmakeMove(game);
  453.                 if (!check) AddLegalMove(game, square, s);
  454.                     /* Move didn't put (or leave) king in check, so it is a
  455.                     ** valid move.  Since it is valid, record it. */
  456.  
  457.                 if (dest) break;    /* Once we hit a piece, we are
  458.                                     ** done in this direction. */
  459.             }
  460.         }
  461.     }
  462.  
  463.     square = (*game)->doc.king[color].kingLoc;
  464.     if (CastleOkay(game, QSIDE)) AddLegalMove(game, square, square - 2);
  465.     if (CastleOkay(game, KSIDE)) AddLegalMove(game, square, square + 2);
  466.         /* If castling possible, add it to move list. */
  467.  
  468.     (**gameMoves)[gameIndex] = cacheMove;
  469.         /* Restore the game move that got clobbered by calculating
  470.         ** the legal move list.
  471.         */
  472. }
  473.  
  474.  
  475.  
  476. /*****************************************************************************/
  477.  
  478.  
  479.  
  480. #pragma segment Chess
  481. void    MakeMove(FileRecHndl game, short moveFrom, short moveTo,
  482.                  short promoteTo, Boolean modifyGame)
  483. {
  484.     GameListHndl    gameMoves;
  485.     long            newHndlSize, oldHndlSize;
  486.     short            gameIndex, numGameMoves, color, rank, i;
  487.     short            pieceMoved, pieceCaptured, pieceCapturedFrom;
  488.     short            absPieceMoved, delta, middle, oldRookLoc;
  489.  
  490.     if (moveFrom == -1) {
  491.         UnmakeMove(game);
  492.         return;
  493.     }
  494.  
  495.     gameIndex    = (*game)->doc.gameIndex;
  496.     numGameMoves = (*game)->doc.numGameMoves;
  497.     gameMoves    = (*game)->doc.gameMoves;
  498.     color        = WhosMove(game);
  499.  
  500.     i = (gameIndex > numGameMoves) ? gameIndex : numGameMoves;
  501.     newHndlSize = ((i | 0x3F) + 1) * sizeof(GameElement);
  502.     oldHndlSize = GetHandleSize((Handle)gameMoves);
  503.     if (newHndlSize != oldHndlSize)
  504.         SetHandleSize((Handle)gameMoves, newHndlSize);
  505.  
  506.     if (moveFrom == 1) {
  507.         moveFrom   = (**gameMoves)[gameIndex].moveFrom;
  508.         moveTo     = (**gameMoves)[gameIndex].moveTo;
  509.         promoteTo  = (**gameMoves)[gameIndex].promoteTo;
  510.         modifyGame = false;
  511.     }
  512.  
  513.     if (!moveFrom) {        /* Draw agreed upon, or player resigned. */
  514.         if (gameIndex)
  515.             if (!(**gameMoves)[gameIndex - 1].moveFrom) --gameIndex;
  516.                 /* In case there is a race condition, only allow one game-ending
  517.                 ** "move" to occur. */
  518.         (**gameMoves)[gameIndex].moveFrom          = 0;
  519.         (**gameMoves)[gameIndex].moveTo            = moveTo;
  520.         (**gameMoves)[gameIndex].pieceCaptured     = 0;
  521.         (**gameMoves)[gameIndex].pieceCapturedFrom = 0;
  522.         (**gameMoves)[gameIndex].promoteTo         = 0;
  523.         (*game)->doc.gameIndex = ++gameIndex;
  524.         (*game)->doc.numGameMoves   = gameIndex;
  525.         (*game)->fileState.docDirty = true;
  526.         (*game)->doc.resync = kResync;
  527.         return;
  528.     }
  529.  
  530.     pieceMoved        = (*game)->doc.theBoard[moveFrom];
  531.     pieceCaptured     = (*game)->doc.theBoard[moveTo];
  532.     pieceCapturedFrom = moveTo;
  533.  
  534.     absPieceMoved = (pieceMoved < 0) ? -pieceMoved : pieceMoved;
  535.  
  536.     if (absPieceMoved == PAWN) {
  537.  
  538.         rank = moveTo / 10;
  539.         if ((rank == 2) || (rank == 9)) {
  540.             if (promoteTo < 0) promoteTo = -promoteTo;
  541.             pieceMoved *= promoteTo;
  542.             promoteTo = pieceMoved;
  543.         }
  544.         else promoteTo = 0;
  545.  
  546.         if (moveTo == (*game)->doc.enPasMove) {
  547.             pieceCaptured     = -pieceMoved;
  548.             pieceCapturedFrom = (*game)->doc.enPasPawnLoc;
  549.         }        /* If pawn move is onto en-passant move square, then the
  550.                 ** capture is from a square other than moveTo.
  551.                 */
  552.     }
  553.     else promoteTo = 0;
  554.  
  555.     (**gameMoves)[gameIndex].moveFrom          = moveFrom;
  556.     (**gameMoves)[gameIndex].moveTo            = moveTo;
  557.     (**gameMoves)[gameIndex].pieceCaptured     = pieceCaptured;
  558.     (**gameMoves)[gameIndex].pieceCapturedFrom = pieceCapturedFrom;
  559.     (**gameMoves)[gameIndex].promoteTo         = promoteTo;
  560.     (*game)->doc.gameIndex = ++gameIndex;
  561.  
  562.     if (modifyGame) {
  563.         (*game)->doc.numGameMoves   = gameIndex;
  564.         (*game)->fileState.docDirty = true;
  565.     }
  566.  
  567.     /* The move has now been recorded in the move list.  Now make the move. */
  568.  
  569.     (*game)->doc.theBoard[pieceCapturedFrom] = 0;
  570.     (*game)->doc.theBoard[moveTo]            = pieceMoved;
  571.     (*game)->doc.theBoard[moveFrom]          = 0;
  572.         /* The move is now made, except for the rook if castling. */
  573.  
  574.     delta  = moveTo - moveFrom;
  575.     middle = (moveTo + moveFrom) / 2;
  576.  
  577.     if (absPieceMoved == KING) {
  578.         oldRookLoc = 0;
  579.         if (delta == -2) oldRookLoc = moveFrom - 4;
  580.         if (delta == 2)  oldRookLoc = moveFrom + 3;
  581.         if (oldRookLoc) {
  582.             (*game)->doc.theBoard[middle] = (*game)->doc.theBoard[oldRookLoc];
  583.             (*game)->doc.theBoard[oldRookLoc] = 0;
  584.         }
  585.     }        /* Castling (and therefore move) now complete. */
  586.  
  587.  
  588.     /* All that remains is some information updating for castling, king
  589.     ** position, and en-passant. */
  590.  
  591.     if (absPieceMoved == KING) {
  592.         (*game)->doc.king[color].kingLoc = moveTo;
  593.         ++(*game)->doc.king[color].kingMoves;
  594.     }
  595.  
  596.     if ((moveFrom==21) || (moveTo==21)) ++(*game)->doc.king[BLACK].rookMoves[QSIDE];
  597.     if ((moveFrom==28) || (moveTo==28)) ++(*game)->doc.king[BLACK].rookMoves[KSIDE];
  598.     if ((moveFrom==91) || (moveTo==91)) ++(*game)->doc.king[WHITE].rookMoves[QSIDE];
  599.     if ((moveFrom==98) || (moveTo==98)) ++(*game)->doc.king[WHITE].rookMoves[KSIDE];
  600.         /* This accounts for all rook moves/captures, other than castling.
  601.         ** This is necessary to keep track of rook moves/captures to determine
  602.         ** if castling is allowed.  Rook moves when castling don't have to be
  603.         ** accounted for, since the king move in the castle will prevent
  604.         ** any more castling.
  605.         */
  606.  
  607.     /* If the move was a double-pawn-push, then we have some en-passant
  608.     ** information to record.  Otherwise, we need to zero-out these values.
  609.     */
  610.  
  611.     if (absPieceMoved == PAWN) {
  612.  
  613.         if ((delta != -20) && (delta != 20))
  614.             middle = moveTo = 0;
  615.                 /* If not a double-pawn-push, then record 0's for the
  616.                 ** en-passant information.  This will prevent en-passant
  617.                 ** moves from being generated.
  618.                 */
  619.  
  620.         (*game)->doc.enPasMove    = middle;
  621.         (*game)->doc.enPasPawnLoc = moveTo;
  622.             /* Record the en-passant information.  These values are
  623.             ** non-zero if the pawn was double-pushed.
  624.             */
  625.     }
  626. }
  627.  
  628.  
  629.  
  630. /*****************************************************************************/
  631.  
  632.  
  633.  
  634. #pragma segment Chess
  635. void    NewGame(FileRecHndl game)
  636. {
  637.     TheDocPtr    docPtr;
  638.  
  639.     docPtr = &(*game)->doc;
  640.  
  641.     newDocData.legalMoves = docPtr->legalMoves;
  642.     newDocData.gameMoves  = docPtr->gameMoves;
  643.  
  644.     *docPtr = newDocData;
  645.  
  646.     newDocData.legalMoves = nil;
  647.     newDocData.gameMoves  = nil;
  648. }
  649.  
  650.  
  651.  
  652. /*****************************************************************************/
  653.  
  654.  
  655.  
  656. #pragma segment Chess
  657. short    SquareAttacked(FileRecHndl game, short square, short color)
  658. {
  659.     short    destColor;
  660.     short    kind;
  661.     short    dirNum, dir;
  662.     short    s, dist, maxDist, dest, taker, takerLoc;
  663.  
  664.     taker    = KING + 1;        /* This is a no-take flag. */
  665.     takerLoc = 0;
  666.  
  667.     for (kind = KNIGHT; kind <= QUEEN; kind += (QUEEN - KNIGHT)) {
  668.         /* Check in the knight and queen directions. */
  669.  
  670.         for (dirNum = 0; dir = direction[kind][dirNum]; ++dirNum) {
  671.             /* The direction we will scan for an attack, for the most part. */
  672.  
  673.             maxDist = (kind == KNIGHT) ? 1 : 7;
  674.             for (s = square, dist = 1; dist <= maxDist; ++dist) {
  675.  
  676.                 s += dir;
  677.                 if ((dest = (*game)->doc.theBoard[s]) == EMPTY) continue;
  678.                     /* Empty square, so keep looking. */
  679.  
  680.                 if (dest == OBNDS) break;
  681.                     /* Can't be attacked from this direction anymore. */
  682.  
  683.                 destColor = BLACK;
  684.                 if (dest < 0) {
  685.                     destColor = WHITE;
  686.                     dest = -dest;
  687.                 }
  688.  
  689.                 if (destColor == color) break;
  690.                     /* Ran into our own piece, so no attack
  691.                     ** from this direction.
  692.                     */
  693.  
  694.                 if (dest >= taker) continue;
  695.  
  696.                 if (kind == KNIGHT)    {        /* If we are looking for knights... */
  697.                     if (dest == KNIGHT) {
  698.                         taker    = KNIGHT;
  699.                         takerLoc = s;
  700.                         dirNum   = 7;        /* Since there is 'an' attack by a knight,
  701.                                             ** we don't care if there is another.
  702.                                             ** We have already established a knight
  703.                                             ** take, so we can skip the rest of the
  704.                                             ** knight directions. */
  705.                     }
  706.                     continue;        /* Only knights can take in this direction. */
  707.                 }
  708.  
  709.                 if (dest == KING) {
  710.                     if (dist == 1) {
  711.                         taker    = KING;
  712.                         takerLoc = s;
  713.                     }
  714.                     break;
  715.                 }            /* We are looking in the non-knight move directions
  716.                             ** for attackers.  If a king is found as the possible
  717.                             ** attacker, make sure it is in range (one square away)
  718.                             ** before counting it as an attack.  If it isn't one
  719.                             ** square away, then it serves to prevent any other
  720.                             ** attack from this direction.
  721.                             */
  722.  
  723.                 if (dest == QUEEN) {
  724.                     taker    = QUEEN;
  725.                     takerLoc = s;
  726.                     break;
  727.                 }            /* If the potential attacker is a queen, then it is
  728.                             ** a valid attacker.
  729.                             */
  730.  
  731.                 if (dest == ROOK) {
  732.                     if (dirNum > 3) {
  733.                         taker    = ROOK;
  734.                         takerLoc = s;
  735.                     }
  736.                     break;
  737.                 }            /* If the potential attacker is a rook, and we are
  738.                             ** examining a rank or file, then count it as an attacker.
  739.                             ** Otherwise, the rook serves to prevent any other
  740.                             ** attack from this direction.
  741.                             */
  742.  
  743.                 if (dest == BISHOP) {
  744.                     if (dirNum < 4) {
  745.                         taker    = BISHOP;
  746.                         takerLoc = s;
  747.                     }
  748.                     break;
  749.                 }            /* If the potential attacker is a bishop, and we are
  750.                             ** examining a diagonal, then count it as an attacker.
  751.                             ** Otherwise, the bishop serves to prevent any other
  752.                             ** attack from this direction.
  753.                             */
  754.  
  755.                 if (dest == PAWN) {
  756.                     if (destColor == BLACK) {
  757.                         if ((dirNum < 2) && (dist == 1)) {
  758.                             taker    = PAWN;
  759.                             takerLoc = s;
  760.                         }
  761.                     }
  762.                     else
  763.                         if (
  764.                             (dirNum > 1) && 
  765.                             (dirNum < 4) && 
  766.                             (dist == 1)
  767.                         ) {
  768.                             taker    = PAWN;
  769.                             takerLoc = s;
  770.                         }
  771.                     break;
  772.                 }
  773.  
  774.                 break;
  775.                     /* Final case is a knight in a non-knight direction. */
  776.             }
  777.         }
  778.     }
  779.  
  780.     return(takerLoc);
  781. }
  782.  
  783.  
  784.  
  785. /*****************************************************************************/
  786.  
  787.  
  788.  
  789. #pragma segment Chess
  790. void    UnmakeMove(FileRecHndl game)
  791. {
  792.     GameListHndl    gameMoves;
  793.     short            gameIndex, numGameMoves;
  794.     short            moveFrom, moveTo, pieceCaptured, pieceCapturedFrom;
  795.     short            promoteTo, pieceMoved, color, delta, oldRookLoc, middle;
  796.  
  797.     gameIndex    = (*game)->doc.gameIndex;
  798.     numGameMoves = (*game)->doc.numGameMoves;
  799.     gameMoves    = (*game)->doc.gameMoves;
  800.  
  801.     if (!gameIndex) return;
  802.     --gameIndex;
  803.  
  804.     moveFrom          = (**gameMoves)[gameIndex].moveFrom;
  805.     moveTo            = (**gameMoves)[gameIndex].moveTo;
  806.     pieceCaptured     = (**gameMoves)[gameIndex].pieceCaptured;
  807.     pieceCapturedFrom = (**gameMoves)[gameIndex].pieceCapturedFrom;
  808.     promoteTo         = (**gameMoves)[gameIndex].promoteTo;
  809.  
  810.     if (moveFrom) {        /* The "move" could be a resign or draw.  Make sure it isn't. */
  811.         pieceMoved = (*game)->doc.theBoard[moveTo];
  812.         if (promoteTo) pieceMoved = (pieceMoved < 0) ? -1 : 1;
  813.  
  814.         (*game)->doc.theBoard[moveFrom]          = pieceMoved;
  815.         (*game)->doc.theBoard[moveTo]            = 0;
  816.         (*game)->doc.theBoard[pieceCapturedFrom] = pieceCaptured;
  817.             /* Any move now undone, except for castling.  The rook still has
  818.             ** to be put back.
  819.             */
  820.  
  821.         if ((pieceMoved == KING) || (pieceMoved == -KING)) {
  822.  
  823.             color = ((gameIndex + (*game)->doc.startColor) & 0x01);        /* Who's move it is. */
  824.             (*game)->doc.king[color].kingLoc = moveFrom;
  825.             --(*game)->doc.king[color].kingMoves;
  826.  
  827.             delta  = moveTo - moveFrom;
  828.             oldRookLoc = 0;
  829.             if (delta == -2) oldRookLoc = moveFrom - 4;
  830.             if (delta == 2)  oldRookLoc = moveFrom + 3;
  831.             if (oldRookLoc) {
  832.                 middle = (moveTo + moveFrom) / 2;
  833.                 (*game)->doc.theBoard[oldRookLoc] = (*game)->doc.theBoard[middle];
  834.                 (*game)->doc.theBoard[middle] = 0;
  835.             }
  836.         }        /* Castling now completely undone. */
  837.  
  838.         if ((moveFrom == 21) || (moveTo == 21)) --(*game)->doc.king[BLACK].rookMoves[QSIDE];
  839.         if ((moveFrom == 28) || (moveTo == 28)) --(*game)->doc.king[BLACK].rookMoves[KSIDE];
  840.         if ((moveFrom == 91) || (moveTo == 91)) --(*game)->doc.king[WHITE].rookMoves[QSIDE];
  841.         if ((moveFrom == 98) || (moveTo == 98)) --(*game)->doc.king[WHITE].rookMoves[KSIDE];
  842.             /* Undo any rook move/capture accounting.  This info is used when
  843.             ** determining of castling is allowed.
  844.             */
  845.  
  846.         (*game)->doc.enPasMove = (*game)->doc.enPasPawnLoc = 0;
  847.             /* Assume move previous to the one we just undid was not a
  848.             ** double-pawn-push.  If it was not a double-pawn-push, then
  849.             ** en-passant moves are not possible at the move number we
  850.             ** just undid.
  851.             */
  852.     }
  853.  
  854.     if (gameIndex) {        /* Restore en-passant possibilities. */
  855.  
  856.         moveFrom   = (**gameMoves)[--gameIndex].moveFrom;
  857.         moveTo     = (**gameMoves)[gameIndex++].moveTo;
  858.         pieceMoved = (*game)->doc.theBoard[moveTo];
  859.  
  860.         if ((pieceMoved == PAWN) || (pieceMoved == -PAWN)) {
  861.             delta = moveTo - moveFrom;
  862.             if ((delta == -20) || (delta == 20)) {
  863.                 (*game)->doc.enPasMove    = (moveTo + moveFrom) / 2;
  864.                 (*game)->doc.enPasPawnLoc = moveTo;
  865.             }
  866.         }
  867.     }
  868.     else {
  869.         (*game)->doc.enPasMove    = (*game)->doc.arngEnPasMove;
  870.         (*game)->doc.enPasPawnLoc = (*game)->doc.arngEnPasPawnLoc;
  871.     }
  872.  
  873.     (*game)->doc.gameIndex = gameIndex;
  874. }
  875.  
  876.  
  877.  
  878.  
  879. /*****************************************************************************/
  880.  
  881.  
  882.  
  883. #pragma segment Chess
  884. short    UpdateTime(FileRecHndl game, Boolean canLose)
  885. {
  886.     FileRecPtr    frPtr;
  887.     short        moveColor, myColor;
  888.     long        timeLeft, opponentTimeLeft, oldTimeLeft, diff;
  889.  
  890.     frPtr = *game;
  891.     moveColor        = WhosMove(game);
  892.     myColor          = frPtr->doc.myColor;
  893.     timeLeft         = frPtr->doc.timeLeft[moveColor];
  894.     opponentTimeLeft = frPtr->doc.timeLeft[moveColor ^ 1];
  895.  
  896.     if (!frPtr->doc.twoPlayer) myColor = moveColor;
  897.         /* Since we are not playing over the net, both sides can lose 
  898.         ** due to time on this machine. */
  899.  
  900.     if ((timeLeft > 0) && (opponentTimeLeft > 0)) {
  901.         oldTimeLeft = timeLeft;
  902.         diff = TickCount() - frPtr->doc.timerRefTick;
  903.         if (diff < 0) {
  904.             frPtr->doc.timerRefTick = TickCount();
  905.             diff = 0;
  906.         }
  907.         diff /= 60;
  908.         diff *= 60;
  909.         if (diff >= 60) {
  910.             if (!GameStatus(game)) {
  911.                 timeLeft -= diff;
  912.                 if (timeLeft < 60) timeLeft = 0;
  913.                 if (!timeLeft)
  914.                     if ((myColor != moveColor) || (!canLose)) timeLeft = 60;
  915.                 frPtr->doc.timeLeft[moveColor] = timeLeft;
  916.                 frPtr->doc.timerRefTick += diff;
  917.                 if (!timeLeft) return(2);
  918.                 if (timeLeft != oldTimeLeft) return(1);
  919.             }
  920.             else (*game)->doc.timerRefTick = TickCount();
  921.                 /* Someone has already lost, so no time change. */
  922.         }
  923.     }
  924.     else (*game)->doc.timerRefTick = TickCount();
  925.  
  926.     return(0);
  927. }
  928.  
  929.  
  930.  
  931. /*****************************************************************************/
  932.  
  933.  
  934.  
  935. #pragma segment Chess
  936. short    WhosMove(FileRecHndl game)
  937. {
  938.     short    color;
  939.  
  940.     color  = ((*game)->doc.gameIndex ^ (*game)->doc.startColor);
  941.     return(color & 0x01);
  942. }
  943.  
  944.  
  945.  
  946. /*****************************************************************************/
  947.  
  948.  
  949.  
  950. #pragma segment Chess
  951. void    EndTheGame(FileRecHndl game, short endReason)
  952. {
  953.     WindowPtr    oldPort;
  954.  
  955.     MakeMove(game, 0, endReason, 0, true);
  956.         /* Record who resigned or draw agreement. */
  957.  
  958.     oldPort = SetFilePort(game);
  959.     ImageDocument(game, true);
  960.     AdjustGameSlider(game);
  961.     UpdateGameStatus(game);
  962.     SetPort(oldPort);
  963. }
  964.  
  965.  
  966.  
  967. /*****************************************************************************/
  968.  
  969.  
  970.  
  971. #pragma segment Chess
  972. void    UpdateGameStatus(FileRecHndl game)
  973. {
  974.     WindowPtr        oldPort;
  975.     ControlHandle    draw, resign;
  976.     short            status, myColor;
  977.     Rect            drawRect, resignRect, workRect;
  978.     Point            endOfText;
  979.     Boolean            hideEm, hidden;
  980.     Str255            reasonText;
  981.  
  982.     if ((*game)->doc.arrangeBoard) return;
  983.  
  984.     draw   = (*game)->doc.draw;
  985.     resign = (*game)->doc.resign;
  986.  
  987.     if (!draw) return;
  988.  
  989.     oldPort = SetFilePort(game);
  990.  
  991.     drawRect   = (*draw)->contrlRect;
  992.     resignRect = (*resign)->contrlRect;
  993.  
  994.     hideEm = false;
  995.     if (status = GameStatus(game)) hideEm = true;
  996.  
  997.     hidden = false;
  998.     if (drawRect.top & 0x4000) hidden = true;
  999.  
  1000.     workRect = drawRect;
  1001.     if (hidden) OffsetRect(&workRect, 0, -0x4000);
  1002.     workRect.right = resignRect.right;
  1003.  
  1004.     if (hideEm != hidden) {
  1005.         EraseRect(&workRect);
  1006.         MoveControl(draw,   drawRect.left,   drawRect.top ^ 0x4000);
  1007.         MoveControl(resign, resignRect.left, resignRect.top ^ 0x4000);
  1008.     }
  1009.  
  1010.     if (hideEm) {
  1011.         myColor = (*game)->doc.myColor;
  1012.         switch (status) {
  1013.             case kYouWin:
  1014.             case kYouWinOnTime:
  1015.                 status = kWhiteWins + myColor;
  1016.                 break;
  1017.             case kYouLose:
  1018.             case kYouLoseOnTime:
  1019.                 status = kBlackWins - myColor;
  1020.                 break;
  1021.         }
  1022.  
  1023.         TextMode(srcCopy);
  1024.         TextFont(systemFont);
  1025.         GetIndString(reasonText, rGameStat, status);
  1026.         MoveTo(workRect.left, workRect.top + 14);
  1027.         DrawString(reasonText);
  1028.         TextMode(srcOr);
  1029.         GetPen(&endOfText);
  1030.         workRect.left = endOfText.h;
  1031.         EraseRect(&workRect);
  1032.     }
  1033.  
  1034.     SetPort(oldPort);
  1035. }
  1036.  
  1037.  
  1038.  
  1039. /*****************************************************************************/
  1040.  
  1041.  
  1042.  
  1043. #pragma segment Chess
  1044. void    DrawButtonTitle(FileRecHndl game, short newVal)
  1045. {
  1046.     WindowPtr        oldPort;
  1047.     ControlHandle    draw;
  1048.     Rect            drawRect;
  1049.     Str255            drawButtonText;
  1050.  
  1051.     if (newVal != (*game)->doc.drawBtnState) {
  1052.  
  1053.         oldPort = SetFilePort(game);
  1054.  
  1055.         GetIndString(drawButtonText, rGameStat, kDrawButtonText + newVal);
  1056.  
  1057.         draw = (*game)->doc.draw;
  1058.         SetCTitle(draw, drawButtonText);
  1059.         (*game)->doc.drawBtnState = newVal;
  1060.  
  1061.         drawRect = (*draw)->contrlRect;
  1062.         ValidRect(&drawRect);
  1063.  
  1064.         SetPort(oldPort);
  1065.     }
  1066. }
  1067.  
  1068.  
  1069.  
  1070. /*****************************************************************************/
  1071. /*****************************************************************************/
  1072.  
  1073.  
  1074.  
  1075. #pragma segment Chess
  1076. Boolean    ComputerMove(FileRecHndl game)
  1077. {
  1078.     short            numLegalMoves, moveNum;
  1079.     MoveListHndl    legalMoves;
  1080.  
  1081.     numLegalMoves = (*game)->doc.numLegalMoves;
  1082.     legalMoves    = (*game)->doc.legalMoves;
  1083.         /* The list of legal moves has already been generated. */
  1084.  
  1085.     if (numLegalMoves) {        /* If there is a move, pick one, any one. */
  1086.         if ((moveNum = CheckForMate(game, 0, 2)) == -1)
  1087.             moveNum = BestMove(game, 0, 2);        /* Yea, right. */
  1088.         if (moveNum > -1)
  1089.             MakeMove(game,
  1090.                      (**legalMoves)[moveNum].moveFrom,
  1091.                      (**legalMoves)[moveNum].moveTo,
  1092.                      QUEEN, true);
  1093.         return(true);        /* Computer moved. */
  1094.     }
  1095.  
  1096.     return(false);            /* Computer didn't move. */
  1097. }
  1098.  
  1099.  
  1100.  
  1101. /*****************************************************************************/
  1102.  
  1103.  
  1104.  
  1105. #pragma segment Chess
  1106. short    CheckForMate(FileRecHndl game, short nodeDepth, short maxDepth)
  1107. {
  1108.     OSErr                    err;
  1109.     short                    numGameMoves, i, num, gameIndex, ourMove, move;
  1110.     short                    color, kingLoc, result;
  1111.     MoveListHndl            node;
  1112.     GameListHndl            gameMoves;
  1113.     GameElement                cacheMove;
  1114.     EventRecord                event;
  1115.     static MoveListHndl        nodeHndl[10];
  1116.  
  1117.     ourMove = -1;
  1118.     GenerateLegalMoves(game);
  1119.  
  1120.     err = noErr;
  1121.     if (!nodeDepth) {
  1122.         numGameMoves = (*game)->doc.numGameMoves;
  1123.         for (i = 0; i <= maxDepth; ++i) {
  1124.             if (!(nodeHndl[i] = (MoveListHndl)NewHandle(0))) err = memFullErr;
  1125.         }
  1126.     }
  1127.  
  1128.     num  = (*game)->doc.numLegalMoves;
  1129.     node = (*game)->doc.legalMoves;
  1130.  
  1131.     if (!err) {
  1132.         (*game)->doc.legalMoves = nodeHndl[nodeDepth];
  1133.             /* Protect the list of legal moves for this level.  Put a handle
  1134.             ** into the game where moves for the next level can be placed. */
  1135.  
  1136.         gameMoves = (*game)->doc.gameMoves;
  1137.         gameIndex = (*game)->doc.gameIndex;
  1138.         cacheMove = (**gameMoves)[gameIndex];
  1139.             /* Save the game move so we can overwrite that spot. */
  1140.  
  1141.         color = WhosMove(game) ^ 1;
  1142.         if (!(nodeDepth & 0x01)) {
  1143.             for (move = 0; (move < num) && (ourMove == -1); ++move) {
  1144.                 MakeMove(game, (**node)[move].moveFrom, (**node)[move].moveTo, QUEEN, false);
  1145.                 kingLoc = (*game)->doc.king[color].kingLoc;
  1146.                 if (SquareAttacked(game, kingLoc, color)) {        /* The move caused check. */
  1147.                     GenerateLegalMoves(game);
  1148.                     if (!(*game)->doc.numLegalMoves)            /* If no way out of check... */
  1149.                         ourMove = move;                            /* ...it is checkmate. */
  1150.                 }
  1151.                 UnmakeMove(game);
  1152.                 (**gameMoves)[gameIndex] = cacheMove;
  1153.                     /* Restore the game move that got clobbered by trying a move. */
  1154.             }
  1155.             if (idleTick + 10 < TickCount()) {
  1156.                 idleTick = TickCount();
  1157.                 if (EventAvail(everyEvent, &event)) ourMove = -2;
  1158.                 else                                CTEIdle();
  1159.             }
  1160.         }
  1161.  
  1162.         if (nodeDepth < maxDepth) {
  1163.             if (ourMove == -1) {
  1164.                 for (move = 0; (move < num) && (ourMove == -1); ++move) {
  1165.                     MakeMove(game, (**node)[move].moveFrom, (**node)[move].moveTo, QUEEN, false);
  1166.                     result = CheckForMate(game, nodeDepth + 1, maxDepth);
  1167.                     if (result == -1) ourMove = move;
  1168.                         /* If opponent can't find saving move, then this is the move we want. */
  1169.                     if (result == -2) ourMove = -2;
  1170.                         /* User interrupted search. */
  1171.                     UnmakeMove(game);
  1172.                     (**gameMoves)[gameIndex] = cacheMove;
  1173.                         /* Restore the game move that got clobbered by trying a move. */
  1174.                 }
  1175.             }
  1176.         }
  1177.     }
  1178.  
  1179.     if (!nodeDepth) {
  1180.         if (!err) {
  1181.             if (ourMove > -1) {
  1182.                 MakeMove(game, (**node)[ourMove].moveFrom, (**node)[ourMove].moveTo, QUEEN, false);
  1183.                 kingLoc = (*game)->doc.king[color].kingLoc;
  1184.                 if (!SquareAttacked(game, kingLoc, color)) {    /* It is not a mate in 1. */
  1185.                     GenerateLegalMoves(game);
  1186.                     if (!(*game)->doc.numLegalMoves) ourMove = -1;
  1187.                         /* It is a stalemate.  Throw it back. */
  1188.                 }
  1189.                 UnmakeMove(game);
  1190.                 (**gameMoves)[gameIndex] = cacheMove;
  1191.             }
  1192.         }
  1193.  
  1194.         (*game)->doc.numGameMoves = numGameMoves;
  1195.         for (i = 0; i <= maxDepth; ++i) {
  1196.             if (nodeHndl[i]) DisposHandle((Handle)nodeHndl[i]);
  1197.         }
  1198.     }
  1199.  
  1200.     (*game)->doc.numLegalMoves = num;
  1201.     (*game)->doc.legalMoves    = node;
  1202.  
  1203.     return(ourMove);
  1204. }
  1205.  
  1206.  
  1207.  
  1208. /*****************************************************************************/
  1209.  
  1210.  
  1211.  
  1212. #pragma segment Chess
  1213. short    BestMove(FileRecHndl game, short nodeDepth, short maxDepth)
  1214. {
  1215.     OSErr                    err;
  1216.     short                    bestMove, keepBestMove, num, numGameMoves;
  1217.     short                    i, gameIndex, color, move, from, to;
  1218.     long                    value, max;
  1219.     MoveListHndl            node;
  1220.     GameListHndl            gameMoves;
  1221.     GameElement                cacheMove;
  1222.     EventRecord                event;
  1223.     static MoveListHndl        nodeHndl[10];
  1224.  
  1225.     bestMove = -1;
  1226.  
  1227.     num  = (*game)->doc.numLegalMoves;
  1228.     node = (*game)->doc.legalMoves;
  1229.  
  1230.     err = noErr;
  1231.     if (!nodeDepth) {        /* Someday this may be recursive. */
  1232.         numGameMoves = (*game)->doc.numGameMoves;
  1233.         for (i = 0; i <= maxDepth; ++i) {
  1234.             if (!(nodeHndl[i] = (MoveListHndl)NewHandle(0))) err = memFullErr;
  1235.         }
  1236.     }
  1237.  
  1238.     if (!err) {
  1239.         (*game)->doc.legalMoves = nodeHndl[nodeDepth];
  1240.             /* Protect the list of legal moves for this node.  Put a different handle
  1241.             ** into the game where moves for the next level can be placed. */
  1242.  
  1243.         color = WhosMove(game);
  1244.         for (max = 0x80000000L, move = 0; move < num; ++move) {
  1245.             from = (**node)[move].moveFrom;
  1246.             to   = (**node)[move].moveTo;
  1247.             (**node)[move].value = value = OneDeepEval(game, from, to, color);
  1248.             if (max < value) {
  1249.                 max = value;
  1250.                 bestMove = move;        /* Best move so far. */
  1251.             }
  1252.             if (idleTick + 10 < TickCount()) {
  1253.                 idleTick = TickCount();
  1254.                 if (EventAvail(everyEvent, &event)) {
  1255.                     bestMove = -2;
  1256.                     break;
  1257.                 }
  1258.                 CTEIdle();
  1259.                 DoIdleTasks(false);
  1260.             }
  1261.         }
  1262.  
  1263.         if (!nodeDepth) {                /* Play a little defense.  Make sure we */
  1264.             if (bestMove > -1) {        /* aren't getting mated in two. */
  1265.                 gameMoves = (*game)->doc.gameMoves;
  1266.                 gameIndex = (*game)->doc.gameIndex;
  1267.                 cacheMove = (**gameMoves)[gameIndex];
  1268.                     /* Save the game move so we can overwrite that spot. */
  1269.                 for (keepBestMove = bestMove;;) {
  1270.                     from = (**node)[bestMove].moveFrom;
  1271.                     to   = (**node)[bestMove].moveTo;
  1272.                     MakeMove(game, from, to, QUEEN, false);
  1273.                     i = CheckForMate(game, 0, 2);    /* Check for mate in 2. */
  1274.                     UnmakeMove(game);
  1275.                     (**gameMoves)[gameIndex] = cacheMove;
  1276.                     if (i == -2) {
  1277.                         bestMove = -2;
  1278.                         break;        /* User wants to do something, so interrupt. */
  1279.                     }
  1280.                     if (i == -1) break;        /* Opponent has no mate in 2 against bestMove. */
  1281.                     (**node)[bestMove].value = max = 0x80000000L;    /* Getting mated is bad-bad. */
  1282.                     for (i = 0; i < num; ++i) {        /* Try the next best move. */
  1283.                         if (max < (**node)[i].value) {
  1284.                             max = (**node)[i].value;
  1285.                             bestMove = i;
  1286.                         }
  1287.                     }
  1288.                     if (max == 0x80000000L) {
  1289.                         bestMove = keepBestMove;    /* We are going to get mated.  (Bummer.) */
  1290.                         break;
  1291.                     }
  1292.                 }
  1293.             }
  1294.         }
  1295.     }
  1296.  
  1297.     if (!nodeDepth) {            /* Clean up. */
  1298.         (*game)->doc.numGameMoves = numGameMoves;
  1299.         for (i = 0; i <= maxDepth; ++i) {
  1300.             if (nodeHndl[i]) DisposHandle((Handle)nodeHndl[i]);
  1301.         }
  1302.     }
  1303.  
  1304.     (*game)->doc.numLegalMoves = num;
  1305.     (*game)->doc.legalMoves    = node;
  1306.  
  1307.     return(bestMove);
  1308. }
  1309.  
  1310.  
  1311.  
  1312. /*****************************************************************************/
  1313.  
  1314.  
  1315.  
  1316. #pragma segment Chess
  1317. long    OneDeepEval(FileRecHndl game, short from, short to, short color)
  1318. {
  1319.     short            gameIndex, material, matBalance, xcngVal, posVal;
  1320.     short            take, takerLoc, retakerLoc;
  1321.     short            val, saveBoard;
  1322.     short            *boardPtr, keepBoard[120], square, j, k, xcolor;
  1323.     short            movedPiece, piece, pieceColor, r, c, delta, xcng[64], xnum, loop;
  1324.     short            dirNum, dir, s, dist;
  1325.     long            value;
  1326.     GameListHndl    gameMoves;
  1327.     GameElement        cacheMove;
  1328.  
  1329.     gameMoves = (*game)->doc.gameMoves;
  1330.     gameIndex = (*game)->doc.gameIndex;
  1331.     cacheMove = (**gameMoves)[gameIndex];
  1332.         /* Save the game move so we can overwrite that spot. */
  1333.  
  1334.     MakeMove(game, from, to, QUEEN, false);
  1335.  
  1336.     boardPtr   = &(*game)->doc.theBoard[0];
  1337.     matBalance = xcngVal = posVal = 0;
  1338.  
  1339.     for (material = 0, square = START_IBNDS; square < END_IBNDS; ++square) {
  1340.         if (piece = boardPtr[square]) {
  1341.             if (piece != OBNDS) {
  1342.                 if (piece < 0) piece = -piece;
  1343.                 if (piece == KING)   piece = 0;
  1344.                 if (piece == QUEEN)  piece = 9;
  1345.                 if (piece == ROOK)   piece = 5;
  1346.                 if (piece == KNIGHT) piece = 3;
  1347.                 material += piece;
  1348.             }
  1349.         }
  1350.     }
  1351.  
  1352.     movedPiece = boardPtr[to];
  1353.     if (movedPiece < 0) movedPiece = -movedPiece;
  1354.  
  1355.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  1356.  
  1357.         if (piece = boardPtr[square]) {        /* Evaluate value of this piece being here. */
  1358.  
  1359.             if ((piece) && (piece != OBNDS)) {
  1360.  
  1361.                 pieceColor = BLACK;    /* Figure who's piece it is. */
  1362.                 if (piece < 0) {
  1363.                     pieceColor = WHITE;
  1364.                     piece = -piece;
  1365.                 }
  1366.  
  1367.                 j = piece;
  1368.                 if (j == QUEEN)  j = 9;
  1369.                 if (j == ROOK)   j = 5;
  1370.                 if (j == KNIGHT) j = 3;
  1371.                 if (color == pieceColor) matBalance += j;
  1372.                 else                     matBalance -= j;
  1373.  
  1374.                 r = square / 10;    /* Get row and column of piece. */
  1375.                 c = square - 10 * r - 1;
  1376.                 r -= 2;
  1377.  
  1378.                 if (pieceColor == WHITE) r = 7 - r;        /* Flip rank for white. */
  1379.  
  1380.                 if (piece == PAWN) {            /* Weight the pawn position. */
  1381.                     if (r > 4) {
  1382.                         if (r == 6) r = 63;        /* Highly advanced pawns are nice. */
  1383.                         if (r == 5) r = 30;
  1384.                     }
  1385.                     else {
  1386.                         if (material > 40) {
  1387.                             if (r < 2) c = 0;
  1388.                             if (c > 3) c = 7 - c;
  1389.                             switch (c) {
  1390.                                 case 0:
  1391.                                     r = 0;
  1392.                                     break;
  1393.                                 case 1:
  1394.                                     if (r > 2) r = -8;
  1395.                                     else       r = 0;
  1396.                                     break;
  1397.                                 case 2:
  1398.                                     if (r == 2) r = -2;
  1399.                                     break;
  1400.                                 case 3:
  1401.                                     if (r == 2) r = 6;
  1402.                                     if (r == 3) r = 8;
  1403.                                     break;
  1404.                             }
  1405.                         }
  1406.                         if (material < 26) r *= 4;
  1407.                     }
  1408.                     for (j = 9; j <= 11; j += 2) {    /* Give weight to pawn chains. */
  1409.                         k = boardPtr[square + j];
  1410.                         if (pieceColor == WHITE) k = -k;
  1411.                         if (k == PAWN) {
  1412.                             k = boardPtr[square - j];
  1413.                             if (pieceColor == WHITE) k = -k;
  1414.                             if (k == PAWN) ++r;
  1415.                         }
  1416.                     }
  1417.                     for (j = square;;) {    /* Give negative weight for doubled pawns. */
  1418.                         j += 10;
  1419.                         if (pieceColor == WHITE) j -= 20;
  1420.                         k = boardPtr[j];
  1421.                         if (k == OBNDS) break;
  1422.                         if (pieceColor == WHITE) k = -k;
  1423.                         if (k == PAWN) --r;
  1424.                     }
  1425.                     if (
  1426.                         ((*game)->doc.gameIndex == 2) &&
  1427.                         (square == 54) &&
  1428.                         ((boardPtr[63] == WP) || (boardPtr[65] == WP))
  1429.                     ) {
  1430.                         r = -200;        /* Special-case out center-counter. */
  1431.                     }
  1432.                     if (color != pieceColor) r = -r;
  1433.                     posVal += r;
  1434.                 }
  1435.  
  1436.                 if (piece == KNIGHT) {        /* Give weight to centralized knights. */
  1437.                     if ((!r) || (r == 7)) {
  1438.                         r = -4;
  1439.                         if ((*game)->doc.gameIndex < 3) {
  1440.                             r = 200;
  1441.                             /* Special-case out knights early, 'cause it's really gross. */
  1442.                         }
  1443.                     }
  1444.                     else {
  1445.                         if (r == 4) r = 5;
  1446.                         if (r > 3) r = 7 - r;
  1447.                         if ((c < 2) || (c > 5))  r /= 2;
  1448.                         if ((c < 1) || (c > 6))  r /= 2;
  1449.                     }
  1450.                     if (color != pieceColor) r = -r;
  1451.                     posVal += r;
  1452.                 }
  1453.  
  1454.                 if (piece == BISHOP) {
  1455.                     j = r;
  1456.                     if (r > 5) r = 7 - r;
  1457.                         if (!r) r = -2;        /* Get those bishops developed. */
  1458.                     if ((c < 1) || (c > 6))  r /= 4;
  1459.                     if (j > 1) {        /* Give weight to knights before bishops past 2nd rank. */
  1460.                         c = (c < 4) ? 2 : 7;
  1461.                         k = boardPtr[90 + c - 70 * pieceColor];
  1462.                         if (pieceColor == WHITE) k = -k;
  1463.                         if (k == KNIGHT) r = -1;
  1464.                     }
  1465.                     if (color != pieceColor) r = -r;
  1466.                     posVal += r;
  1467.                 }
  1468.  
  1469.                 if (piece == ROOK) {    /* Give weight to rooks on open files. */
  1470.                     if (material > 25) {
  1471.                         delta = (color == BLACK) ? 10 : -10;
  1472.                         if ((c) && (c < 7)) {
  1473.                             for (j = square + delta; !boardPtr[j]; j += delta, ++r);
  1474.                             if (color != pieceColor) r = -r;
  1475.                             posVal += r;
  1476.                         }
  1477.                     }
  1478.                 }
  1479.  
  1480.                 if (piece == QUEEN) {
  1481.                     if (color == pieceColor) {
  1482.                         if (movedPiece == QUEEN) {
  1483.                             if (material > 50) {
  1484.                                 r = -2;
  1485.                                 if (color != pieceColor) r = -r;
  1486.                                 posVal += r;
  1487.                             }        /* Keep the queen from moving too much in the beginning. */
  1488.                         }
  1489.                     }
  1490.                 }
  1491.                 if (piece == KING) {    /* Give weight to no king moves other than castling. */
  1492.                     j = 0;
  1493.                     if ((*game)->doc.king[color].kingMoves < 2) {
  1494.                         j = -10;
  1495.                         if (!r) {
  1496.                             if (c == 6) j = 6;
  1497.                             if (c == 2) j = 4;
  1498.                         }
  1499.                     }
  1500.                     if (color != pieceColor) j = -j;
  1501.                     posVal += j;
  1502.                 }
  1503.  
  1504.                 if (color == pieceColor) {
  1505.  
  1506.                     for (dirNum = 0; dir = direction[piece][dirNum]; ++dirNum) {
  1507.                         for (s = square, dist = 1; dist <= distance[piece]; ++dist) {
  1508.                             if (piece == PAWN) {
  1509.                                 if (dirNum > 1) break;
  1510.                                 dir = direction[BISHOP][dirNum];
  1511.                                 if (color == BLACK) dir = -dir;
  1512.                             }
  1513.                             s += dir;
  1514.                             if ((take = (*game)->doc.theBoard[s]) == EMPTY) continue;
  1515.                             if (take == OBNDS) break;
  1516.                             xcolor = BLACK;
  1517.                             if (take < 0) {
  1518.                                 xcolor = WHITE;
  1519.                                 take = -take;
  1520.                             }
  1521.                             if (xcolor == color) break;
  1522.                             switch (j = take) {
  1523.                                 case KNIGHT:
  1524.                                     j = 3;
  1525.                                     break;
  1526.                                 case ROOK:
  1527.                                     j = 5;
  1528.                                     break;
  1529.                                 case QUEEN:
  1530.                                     j = 9;
  1531.                                     break;
  1532.                                 case KING:
  1533.                                     j = material;
  1534.                                     if (j > 39) j = 60 - j;
  1535.                                     j /= 2;
  1536.                                     break;
  1537.                             }
  1538.                             switch (k = piece) {
  1539.                                 case KNIGHT:
  1540.                                     k = 3;
  1541.                                     break;
  1542.                                 case ROOK:
  1543.                                     k = 5;
  1544.                                     break;
  1545.                                 case QUEEN:
  1546.                                     k = 9;
  1547.                                     break;
  1548.                                 case KING:
  1549.                                     k = material;
  1550.                                     if (k > 39) k = 60 - k;
  1551.                                     k /= 2;
  1552.                                     break;
  1553.                             }
  1554.                             if (j > k) posVal += (j - k) * 5;
  1555.                             else if (!SquareAttacked(game, s, color)) posVal += (j + 4);
  1556.                             break;
  1557.                         }
  1558.                     }        /* The above code calculates the aggression factor of the board.
  1559.                             ** If the attacking piece is smaller than the piece attacked, then
  1560.                             ** we add the delta value as a plus.  This factor finds forks,
  1561.                             ** chases kings, queens, etc. */
  1562.  
  1563.                             /* The below code estimates the exchange value of takes.  This
  1564.                             ** is only an estimate, as it is not an actual move analysis
  1565.                             ** of all the possible exchanges.  We are only trying to get
  1566.                             ** an estimate of the board position in this function. */
  1567.                     if (takerLoc = SquareAttacked(game, square, color)) {
  1568.                         saveBoard = false;
  1569.                         xnum = val = 0;
  1570.                         for (xcolor = color ^ 1; takerLoc; xcolor ^= 1) {
  1571.                             take = boardPtr[square];
  1572.                             if (take < 0) take = -take;
  1573.                             if (take == KING)   take = 512;
  1574.                             if (take == QUEEN)  take = 9;
  1575.                             if (take == ROOK)   take = 5;
  1576.                             if (take == KNIGHT) take = 3;
  1577.                             retakerLoc = SquareAttacked(game, square, xcolor);
  1578.                             if (xcolor == color) val += take;
  1579.                             else                 val -= take;
  1580.                             xcng[xnum++] = val;
  1581.                             if (retakerLoc) {
  1582.                                 if (!saveBoard) {
  1583.                                     saveBoard = true;
  1584.                                     for (j = START_IBNDS; j < END_IBNDS; ++j)
  1585.                                         keepBoard[j] = boardPtr[j];
  1586.                                 }
  1587.                                 boardPtr[square] = boardPtr[takerLoc];
  1588.                                 boardPtr[takerLoc] = 0;
  1589.                             }
  1590.                             takerLoc = retakerLoc;
  1591.                         }
  1592.                         if (saveBoard)
  1593.                             for (j = START_IBNDS; j < END_IBNDS; ++j)
  1594.                                 boardPtr[j] = keepBoard[j];
  1595.                         xcng[xnum] = xcng[xnum - 1];
  1596.                         xcolor = 1;        /* Check on opponent first. */
  1597.                         for (loop = true; loop; xcolor ^= 1) {
  1598.                             val  = xcng[xcolor];
  1599.                             k    = xnum;
  1600.                             loop = false;
  1601.                             for (j = xcolor + 2; j <= k; j += 2) {
  1602.                                 if (xcolor) {
  1603.                                     if (val > xcng[j]) {
  1604.                                         val = xcng[j];
  1605.                                         xnum = j - 1;
  1606.                                         loop = true;
  1607.                                     }
  1608.                                 }
  1609.                                 else {
  1610.                                     if (val < xcng[j]) {
  1611.                                         val = xcng[j];
  1612.                                         xnum = j - 1;
  1613.                                         loop = true;
  1614.                                     }
  1615.                                 }
  1616.                             }
  1617.                         }
  1618.                         if (xcngVal > val) xcngVal = val;
  1619.                     }
  1620.                 }
  1621.             }
  1622.         }
  1623.     }
  1624.  
  1625.     val = GameStatus(game);
  1626.     j = (100 - (*game)->doc.numLegalMoves) / 3;
  1627.     if (material > 60) j = 0;
  1628.     posVal += j;
  1629.  
  1630.     if ((val >= kStalemate) && (val <= kDrawByRep))
  1631.         matBalance = xcngVal = posVal = 0;
  1632.             /* Drawing may be our best move, so don't just eliminate this move. */
  1633.  
  1634.     UnmakeMove(game);
  1635.     (**gameMoves)[gameIndex] = cacheMove;
  1636.         /* Restore the game move that got clobbered by trying a move. */
  1637.     GenerateLegalMoves(game);
  1638.  
  1639.     value = ((matBalance + xcngVal) << 16) + (posVal << 3);
  1640.     if (material > 29) value += (Random() & 0x1F);
  1641.  
  1642.     return(value);        
  1643. }
  1644.  
  1645.  
  1646.  
  1647.